package com.theme.common.third.apple;

import com.alibaba.fastjson.JSON;
import com.auth0.jwk.InvalidPublicKeyException;
import com.auth0.jwk.Jwk;
import com.theme.common.base.JSONHandler;
import com.theme.common.third.apple.bean.CusJws;
import com.theme.common.third.apple.bean.JwsPayload;
import io.jsonwebtoken.*;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.security.PublicKey;
import java.util.List;
import java.util.Map;

/**
 * @name: AppleIdAccountValidationService <tb>
 * @title: https://mp.weixin.qq.com/s/zISU30KqgQDbeKMhMsjzYQ  <tb>
 * @author: cuixinfu@51play.com <tb>
 * @date: 2021/10/22 21:28:11 <tb>
 */
public class AppleLoginHandler2 {

    private static Logger logger = LoggerFactory.getLogger(AppleLoginHandler2.class);

    private final static int APPLE_ID_PUBLIC_KEY_EXPIRE = 24; //24h

//    @Autowired
//    private StringRedisUtils stringRedisUtils;

    public AppleRpcResult isValid(String accessToken) {
        //校验基本信息:nonce,iss,aud,exp
        CusJws cusJws = CusJws.getJws(accessToken);
        String cusJwsStr = JSONHandler.getGson().toJson(cusJws);
        logger.info("获取基本信息,解析identityToken结果：{}",cusJwsStr);
        if (cusJws == null) {
            // return false;
            logger.error("failed to get nonce,iss,aud,exp fail!");
            return AppleRpcResult.error("获取苹果验证校验基本CusJws信息失败!");
        }
        //iss
        long curTime = System.currentTimeMillis();
        if (cusJws.getJwsPayload().getExp() * 1000 < curTime) {
            // return false;
            logger.error("Apple idToken expired:{}",cusJws.getJwsPayload().getExp());
            return AppleRpcResult.error("苹果登陆授权 idToken 已过期");
        }
        if (!JwsPayload.ISS.equals(cusJws.getJwsPayload().getIss())) {
            // return false;
            logger.error("Apple JwsPayload ISS check fail:{}",JwsPayload.ISS);
            return AppleRpcResult.error("校验苹果登陆授权 ISS错误");
        }
        //校验签名
        PublicKey publicKey = this.getAppleIdPublicKey(cusJws.getJwsHeader().getKid());
        if (!this.verifySignature(accessToken, publicKey)) {
            //return false;
            logger.error("failed to get publicKey");
            return AppleRpcResult.error("获取苹果验证公钥校验失败");
        }
        //return true;
        return AppleRpcResult.ok("苹果验证公钥校验成功!");
    }

    //this.verify2(publicKey, accessToken, cusJws.getJwsPayload().getAud(), cusJws.getJwsPayload().getSub())
    public boolean verify2(PublicKey key, String jwt, String audience, String subject) {
        JwtParser jwtParser = Jwts.parser().setSigningKey(key);
        jwtParser.requireIssuer("https://appleid.apple.com");
        jwtParser.requireAudience(audience);
        jwtParser.requireSubject(subject);
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
            if (claim != null && claim.getBody().containsKey("auth_time")) {
                return true;
            }
            return false;
        } catch (ExpiredJwtException e) {
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * verify signature
     * @param accessToken
     * @return
     */
    private boolean verifySignature(String accessToken, PublicKey publicKey) {
        JsonWebSignature jsonWebSignature = new JsonWebSignature();
        jsonWebSignature.setKey(publicKey);
        try {
            jsonWebSignature.setCompactSerialization(accessToken);
            return jsonWebSignature.verifySignature();
        } catch (JoseException e) {
            return false;
        }
    }

    /**
     * publicKey会本地缓存1天
     * @return
     */
    private PublicKey getAppleIdPublicKey(String kid) {
        String publicKeyStr = null ;
//        publicKeyStr = stringRedisUtils.getString(PhoneConfigure.REDIS_KEY_APPLE_ID_PUBLIC_KEY);
//        if (publicKeyStr == null) {
//            publicKeyStr = this.getAppleIdPublicKeyFromRemote();
//            if (publicKeyStr == null) {
//                return null;
//            }
//            try {
//                PublicKey publicKey = this.publicKeyAdapter(publicKeyStr, kid);
//                stringRedisUtils.setString(PhoneConfigure.REDIS_KEY_APPLE_ID_PUBLIC_KEY, publicKeyStr, APPLE_ID_PUBLIC_KEY_EXPIRE, TimeUnit.HOURS);
//                return publicKey;
//            } catch (Exception ex) {
//                ex.printStackTrace();
//                return null;
//            }
//        }
        return this.publicKeyAdapter(publicKeyStr, kid);
    }

    /**
     * 将appleServer返回的publicKey转换成PublicKey对象
     * @param publicKeyStr
     * @return
     */
    private PublicKey publicKeyAdapter(String publicKeyStr, String kid) {
        if (!StringUtils.hasText(publicKeyStr)) {
            return null;
        }
        Map maps = (Map) JSON.parse(publicKeyStr);
        List<Map> keys = (List<Map>)maps.get("keys");
        Map o = null;
        for (Map key : keys) {
            if (kid.equals(key.get("kid"))) {
                o = key;
                break;
            }
        }
        Jwk jwa = Jwk.fromValues(o);
        try {
            PublicKey publicKey = jwa.getPublicKey();
            return publicKey;
        } catch (InvalidPublicKeyException e) {
            e.printStackTrace();
            logger.error("failed to get publicKey");
            return null;
        }
    }

    /**
     * 从appleServer获取publicKey
     * @return
     */
    private String getAppleIdPublicKeyFromRemote() {
        ResponseEntity<String> responseEntity = new RestTemplate().getForEntity("https://appleid.apple.com/auth/keys", String.class);
        if (responseEntity == null || responseEntity.getStatusCode() != HttpStatus.OK) {
            Object appleIdPublicKeyUrl = null;
            logger.error(String.format("getAppleIdPublicKeyFromRemote [%s] exception, detail:", appleIdPublicKeyUrl));
            return null;
        }
        return responseEntity.getBody();
    }

//    private CusJws getJws(String identityToken) {
//        String[] arrToken = identityToken.split("\\.");
//        if (arrToken == null || arrToken.length != 3) {
//            return null;
//        }
//        Base64.Decoder decoder = Base64.getDecoder();
//        JwsHeader jwsHeader = JSON.parseObject(new String(decoder.decode(arrToken[0])), JwsHeader.class);
//        JwsPayload jwsPayload = JSON.parseObject(new String(decoder.decode(arrToken[1])), JwsPayload.class);
//        return new CusJws(jwsHeader, jwsPayload, arrToken[2]);
//    }
//
//    class CusJws {
//        private JwsHeader jwsHeader;
//        private JwsPayload jwsPayload;
//        private String signature;
//
//        public CusJws(JwsHeader jwsHeader, JwsPayload jwsPayload, String signature) {
//            this.jwsHeader = jwsHeader;
//            this.jwsPayload = jwsPayload;
//            this.signature = signature;
//        }
//
//        public JwsHeader getJwsHeader() {
//            return jwsHeader;
//        }
//        public void setJwsHeader(JwsHeader jwsHeader) {
//            this.jwsHeader = jwsHeader;
//        }
//        public JwsPayload getJwsPayload() {
//            return jwsPayload;
//        }
//        public void setJwsPayload(JwsPayload jwsPayload) {
//            this.jwsPayload = jwsPayload;
//        }
//        public String getSignature() {
//            return signature;
//        }
//        public void setSignature(String signature) {
//            this.signature = signature;
//        }
//    }
//
//    static class JwsHeader {
//        private String kid;
//        private String alg;
//
//        public String getKid() {
//            return kid;
//        }
//        public void setKid(String kid) {
//            this.kid = kid;
//        }
//        public String getAlg() {
//            return alg;
//        }
//        public void setAlg(String alg) {
//            this.alg = alg;
//        }
//    }
//
//    static class JwsPayload {
//        private String iss;
//        private String sub;
//        private String aud;
//        private long exp;
//        private long iat;
//        private String nonce;
//        private String email;
//        private boolean email_verified;
//
//        public final static String ISS = "https://appleid.apple.com";
//
//        public String getIss() {
//            return iss;
//        }
//        public void setIss(String iss) {
//            this.iss = iss;
//        }
//        public String getSub() {
//            return sub;
//        }
//        public void setSub(String sub) {
//            this.sub = sub;
//        }
//        public String getAud() {
//            return aud;
//        }
//        public void setAud(String aud) {
//            this.aud = aud;
//        }
//        public long getExp() {
//            return exp;
//        }
//        public void setExp(long exp) {
//            this.exp = exp;
//        }
//        public long getIat() {
//            return iat;
//        }
//        public void setIat(long iat) {
//            this.iat = iat;
//        }
//        public String getNonce() {
//            return nonce;
//        }
//        public void setNonce(String nonce) {
//            this.nonce = nonce;
//        }
//        public String getEmail() {
//            return email;
//        }
//        public void setEmail(String email) {
//            this.email = email;
//        }
//        public boolean isEmail_verified() {
//            return email_verified;
//        }
//        public void setEmail_verified(boolean email_verified) {
//            this.email_verified = email_verified;
//        }
//    }

    @Test
    public void testMain() {
        // 前端传来的 identityToken（base64编码的JWT）
        // String jwt =
        // "eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MTg1Mjg1LCJpYXQiOjE1ODkxODQ2ODUsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiN1gzc2x2dHVBU0kwYmFSbU0wVGFrQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTE4NDY4NSwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.S9wCOt6EeOoRrSMq4kUkPgJPyP1ruMXEcEZeeQEd1CDpcyVWLI8nTOqrl-l0sWYR-5nl2-1iJyiu77fRv8T7dBoV0EHT7GgM1l7qhnWsI9I8V-56rA9ArdJrLIBJbxu7j-xzQhZb6PZ5MSxPZ6WqZay0RpP9JiQ23ybssWQsMnqzvVZkye0iNtBGT1LnfT80XNxmj8L2uJZY08mXjjWWsYY_h0_IRvqOLyaW99w-F8T9KuDkWz2Z-DJX_tiKC0DOT03ypBv82H0v_v-8lFlp4rNRSB82CdgfYwEWElU7zKZfaHJOxT3wOvRXNpbj6_hENPdbtG2ozgdg2oVEiamz0g";
        String jwt =
                "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MjcwMzI3LCJpYXQiOjE1ODkyNjk3MjcsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoienNIUW9xbTdjcDZOcmxrUHFhTmpGQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI2OTcyNywibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.q5unOzswOjpRYmrVKVm3FRb_Th6kkhgEvoFfTEAIETwgTXZ7bYcQM8J8tCjkGGqtt2z74Z-wTW7Q3ia209xhmwrVDIup0jcQgNTvsCEMkfo9evPIDrNRNQw2Dzw2EBKma8004NL6THYlySoDnPRoW_VQCHP_m0HnjYuIc-wtREEClf-_tOFDPpTsvUFoETHNfhpsLhqj24-zm6MSOocYY3WbUaYJQVEFCz-x6AGko1XkMtms_-JU1xakNtjMZTIVj2XyUI5MO7_eo-D9i_c7Hj-OE9HNBEvFnPxOesDzXvEoYdb7uByXEfa-H1syJMecBMRa3tL76W_CYKsONRkU9Q";
        // String jwt =
        // "eyJraWQiOiJBSURPUEsxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnNreW1pbmcuYXBwbGVsb2dpbmRlbW8iLCJleHAiOjE1NjU2NjU1OTQsImlhdCI6MTU2NTY2NDk5NCwic3ViIjoiMDAwMjY2LmRiZTg2NWIwYWE3MjRlMWM4ODM5MDIwOWI5YzdkNjk1LjAyNTYiLCJhdF9oYXNoIjoiR0ZmODhlX1ptc0pqQ2VkZzJXem85ZyIsImF1dGhfdGltZSI6MTU2NTY2NDk2M30";
        // String jwt =
        // "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MjgzMTc4LCJpYXQiOjE1ODkyODI1NzgsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiV1pLajM1LW44SDVDejBPWVRsWjFXQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI4MjU3OCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.ZXTkeWDnVqJu2F3PPTYFBGEqYeRS7w2Xi2f1k7IBR0sLIlmcYRG2Wm5qIiXn1YXmWOXPc7B6fBHl4KdwG5oRewN0BplLQvuwrbonsIfbSG4GQKxxUdPvGObKLx19N9z8EKCeqNifrVvDXlHLZEDvT04t6U5tz-QWDYOce-yGFd8HjnG4hnKPu98pIwzQMTbCt-c3FTnAID42NBxwaY71DUth3JT5sk7WPY_WRcr0Q3WqX2WO7z_UZnIOWnSCNoLbHVboBPuomwZ_P3klnoQWFbNGHLlVJLroz89W7lMr_c_cxc_cbHexhdGykcxgRWsyW2GbZFdPBQVvbGmvw3beug";

        //String jwt = "eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5Mjg2ODcyLCJpYXQiOjE1ODkyODYyNzIsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiX0ZUSlh3cGhpRFhHeEhIQ1VMODdVZyIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI4NjI3Miwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.c2h4MoEjGWRmJAAppbvuJToTGf8wM510yQYgonZXIwan66KTAmYLE11NpA83xq0pvtuex-hbRjna4WeJWD1QfZsHlZ7iIhL95YoG9y8DXIhTyrd51ADLxn4gEyxOKM03aZ4M54NyoZ13V3osd-T-1tvCX86JZnDlrWkixsUONiXLXB9-G63QO5JwsQPJuorweT9-qj6NIiZmX_ayDhRFpe0FxW41u-c3LhN4dRTb1FMF2LYDymBYsdLNyAv7glDzq13M6rBeQRMRJz7e5C6PcppHhhxgrbJTcdCszB5bjA-Ck8PbnsM2qSxVW6hHd4xEpClEzUMFee8dZIi1PSU0KA";
        //校验基本信息:nonce,iss,aud,exp
        AppleRpcResult valid = this.isValid(jwt);
        System.out.println("苹果登陆授权 idToken 验证结果：" + JSONHandler.objectToJson(valid));
    }
}